Load libraries

library(tidytext) 
library(tidyverse)
library(quanteda)
library(lubridate)
library(ggplot2)
library(plotly)
library(forcats)
library(readxl)
library(leaflet)
library(leaflet.minicharts)
library(sp)
library(sf)
library(readr)
library(stringr)
library(rtweet)
library(ggraph)
library(ggrepel)
library(tidygraph)

Load Data

Parlamentarier <- read_csv("Data/Parlamentarier.csv")

EU-Parlament rausfiltern

Parlamentarier <- Parlamentarier %>% 
  filter(Kategorie!="EU-Parlament")

Read Tabellen mit fehlenden Geschlechtern

alle_geschlechter <- read_excel("Data/Parlamentarier_geschechter.xlsx", sheet = "Geschlechter")

Join fehlende Geschlechterangaben

Parlamentarier <- Parlamentarier %>% 
  left_join(alle_geschlechter, by = c("Name" = "Name"))

Rename Spalten

Parlamentarier <- Parlamentarier %>% 
  rename(Geschlecht = Geschlecht.y)

Bundestag Geschlechterverteilung pro Partei

bundestag <- Parlamentarier %>% 
  select(id, Name, Geschlecht, Partei, Kategorie, SM_Twitter_id, SM_Twitter_user) %>% 
  filter(Kategorie=="Bundestag")

bundestag$Partei[bundestag$Partei == "CSU"] <-"CDU/CSU"
bundestag$Partei[bundestag$Partei=="CDU"]<-"CDU/CSU"

bundestag_man <- bundestag %>% 
  group_by(Partei) %>% 
  filter(Geschlecht == "männlich") %>% 
  summarise(gesamt_man=n())

bundestag_gesamt <- bundestag %>% 
  group_by(Partei) %>% 
  filter(Geschlecht == "weiblich") %>% 
  summarise(gesamt_frau=n()) %>% 
  left_join(bundestag_man) %>% 
  mutate(row_sum=rowSums(.[2:3])) %>% 
  arrange(desc(row_sum))

Barchart Bundestag

  • Stacked-Barchart zur Geschlechterverteilung im Bundestag pro Partei
  • visualisierung der Daten in möglichst geschlechterneuralen Farben
plot_ly(data=bundestag_gesamt, x=~reorder(Partei,-row_sum), y=~gesamt_frau, name="weiblich", marker=list(color=toRGB("#FB7F62")),
        type="bar", hoverinfo="text", text=~paste("",row_sum), 
        hovertemplate=paste("Anzahl: %{y:,.0f}", 
                            "<br>Gesamtanzahl: %{text}")) %>% 
  add_trace(y=~gesamt_man, name="männlich", marker=list(color=toRGB("#775285"))) %>% 
  layout(barmode="stack", title="Geschlechterverteilung in den Parteien im Bundestag", # titel position und größe ändern
         xaxis = list(title = ""), yaxis=list(title="Anzahl Abgeordnete")) %>%
  layout(legend=list(x=1, y=0.5))

Landtag Geschlechterverteilung pro Partei

landtag_partei <- Parlamentarier %>% 
  filter(Kategorie !="Bundestag") %>% 
  filter(Kategorie!="EU-Parlament") %>% 
  filter(Partei !="BIW") %>% 
  filter(Partei!="SSW") %>% 
  filter(Partei!="BVB/FW") %>% 
  filter(Partei!="FW")
landtag_partei$Partei[landtag_partei$Partei == "CSU"] <-"CDU/CSU"
landtag_partei$Partei[landtag_partei$Partei=="CDU"] <- "CDU/CSU"
landtag_partei$Partei[landtag_partei$Partei=="FDP/DVP"] <- "FDP" 
landtag_man <- landtag_partei %>% 
  group_by(Partei) %>% 
  filter(Geschlecht=="männlich") %>% 
  summarise(gesamt_man=n())

landtag_gesamt <- landtag_partei %>% 
  group_by(Partei) %>% 
  filter(Geschlecht=="weiblich") %>% 
  summarise(gesamt_frau=n()) %>% 
  left_join(landtag_man) %>% 
   mutate(row_sum=rowSums(.[2:3])) %>% 
  arrange(desc(row_sum))
Joining, by = "Partei"

Barchart Landtag

  • Stacked-Barchart zur Geschlechterverteilung in den Landtagen pro Partei
  • visualisierung der Daten in möglichst geschlechterneuralen Farben
plot_ly(data=landtag_gesamt, x=~reorder(Partei,-row_sum), y=~gesamt_frau, name="weiblich", marker=list(color=toRGB("#FB7F62")), 
        type="bar", hoverinfo="text", text=~paste("",row_sum), 
        hovertemplate=paste("Anzahl: %{y:,.0f}", 
                            "<br>Gesamtanzahl: %{text}")) %>% 
  add_trace(y=~gesamt_man, name="männlich", marker=list(color=toRGB("#775285"))) %>% 
  layout(barmode="stack", title="Geschlechterverteilung in den Parteien in den Landtagen", # titel position und größe ändern
         xaxis=list(title=""), yaxis=list(title="Anzahl Abgeordnete")) %>%
  layout(legend=list(x=1, y=0.5))

Map mit Geschlechterverteilung in den Landtagen

parlamentarier_landtag <- Parlamentarier %>% 
  filter(Kategorie %in% c("Abgeordnetenhaus von Berlin", 
                          "Bayerischer Landtag", 
                          "Bremische Bürgerschaft", 
                          "Hamburgische Bürgerschaft", 
                          "Hessischer Landtag", 
                          "Landtag Brandenburg", 
                          "Landtag des Saarlandes", 
                          "Landtag Mecklenburg-Vorpommern", 
                          "Landtag Nordrhein-Westfalen", 
                          "Landtag Rheinland-Pfalz", 
                          "Landtag Sachsen-Anhalt", 
                          "Landtag von Baden-Württemberg", 
                          "Niedersächsischer Landtag", 
                          "Sächsischer Landtag", 
                          "Schleswig-Holsteinischer Landtag", 
                          "Thüringer Landtag"))
Kategorie <- c("Abgeordnetenhaus von Berlin", 
               "Bayerischer Landtag", 
               "Bremische Bürgerschaft",
               "Hamburgische Bürgerschaft", 
               "Hessischer Landtag",
               "Landtag Brandenburg", 
               "Landtag des Saarlandes",
               "Landtag Mecklenburg-Vorpommern",
               "Landtag Nordrhein-Westfalen",
               "Landtag Rheinland-Pfalz",
               "Landtag Sachsen-Anhalt", 
               "Landtag von Baden-Württemberg", 
               "Niedersächsischer Landtag", 
               "Sächsischer Landtag", 
               "Schleswig-Holsteinischer Landtag", 
               "Thüringer Landtag")

lat <- c(52.520008,
         48.917431,
         53.074982,
         53.553815,
         50.652052,
         52.408418,
         49.396423,
         53.612651,
         51.433237,
         50.118346,
         51.950265,
         48.661604,
         52.636704,
         51.104541,
         54.219367,
         51.010989)

lng <- c(13.404954,
         11.407980,
         8.807080,
         9.991575,
         9.162438,
         12.562492,
         7.022961,
         12.429595,
         7.661594,
         7.308953,
         11.692274,
         9.350134,
         9.845077,
         13.201738,
         9.696117,
         10.845346)

bundeslaender_koordinaten <- data.frame(Kategorie, lat, lng)
parlamentarier_man <- parlamentarier_landtag %>% 
  group_by(Kategorie) %>% 
  filter(Geschlecht == "männlich") %>% 
  summarise(männlich = n())

parlamentarier_landtag_gesamt <- parlamentarier_landtag %>% 
  group_by(Kategorie) %>% 
  filter(Geschlecht == "weiblich") %>% 
  summarise(weiblich = n()) %>% 
  left_join(parlamentarier_man)
Joining, by = "Kategorie"
parlamentarier_koordinaten <- parlamentarier_landtag_gesamt %>% 
  left_join(bundeslaender_koordinaten, by = c("Kategorie" = "Kategorie"))

Map mit Pie-Charts

colors <- c("#FB7F62", "#775285")

grenzen <- readRDS("Data/gadm36_DEU_1_sp.rds") %>% 
  st_as_sf()

popup_bundeslaender <- paste0("<strong>Bundesland: </strong>", grenzen$NAME_1)
map <- leaflet() %>% 
  addProviderTiles(providers$CartoDB.VoyagerLabelsUnder) %>% 
  setView(lng = 10.451526, lat = 51.165691, zoom = 5.5) %>% 
  addPolygons(data = grenzen,
              fillColor = "grey",
              fillOpacity = 0.1,
              weight = 3,
              color = "grey",
              popup = popup_bundeslaender) %>% 
  addMinicharts(
    parlamentarier_koordinaten$lng, parlamentarier_koordinaten$lat,
    type = "pie",
    chartdata = parlamentarier_koordinaten[, c("weiblich", "männlich")],
    colorPalette = colors,
    width = 46,
    height = 46,
    opacity = 0.8,
    legendPosition = "bottomright"
  )
sf layer has inconsistent datum (+proj=longlat +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +no_defs).
Need '+proj=longlat +datum=WGS84'
map

Tweets der Politiker*innen scrapen

Nicht als ausführbaren Code, damit die Daten nicht nochmals gezogen werden…

Twitter_accounts <- Parlamentarier %>% filter(Kategorie!=“EU-Parlament”) %>% filter(Partei!=“BIW”) %>% filter(Partei!=“SSW”) %>% filter(Partei!=“BVB/FW”) %>% filter(Partei!=“FW”) %>% filter(Partei!=“fraktionslos”) %>% filter(SM_Twitter_user != "")

token <- get_token() token

alle_tweets <- list() userIdList = list(Twitter_accounts\(SM_Twitter_user) rl <- rate_limit(token, "statuses/user_timeline") for(user in userIdList[[1]]){ alle_tweets[[user]] <- get_timeline(user, n = 2, check = F) print("rate limit remaining:", str(rl\)remaining)) print(“at user:”, str(user)) rl <- rl %>% mutate(remaining = remaining - 1) # if rate limit exhausted, then wait to rate limit reset if (rl\(remaining == 5L) { rl <- rate_limit(token, "statuses/user_timeline") print("rate limit exceeded, waiting for 900s at user", str(user)) Sys.sleep(as.numeric(rl\)reset, “secs”)) } }

Nach relevanten Hashtags filtern

  • “alle_tweets.rds” ist die Datei mit allen gezogenen Tweets.
  • nach relevanten Tweets filtern mit vorher bestimmten Hashtags (“Twitter-Hashtags.xlsx”), Tweets in Kleinschreibung formatieren und als “final_tweets.rds” exportieren.
alle_tweets <- read_rds("Data/alle_tweets.rds")

suchbegriffe <- read_excel("Data/Twitter-Hashtags.xlsx", sheet ="Tabelle2")

suchbegriffe_vector <- suchbegriffe$Hashtags

alle_tweets <- bind_rows(alle_tweets)

alle_tweets <- alle_tweets %>% 
  mutate(text = tolower(text))

filtert_tweets <- alle_tweets %>% 
  filter(str_detect(text, paste (suchbegriffe_vector, collapse = "|")))

write_rds(filtert_tweets, "final_tweets.rds")

Sentimentanalyse

Parlamentarier_senti <- Parlamentarier %>% 
  filter(Partei!="BIW") %>% 
  filter(Partei!="SSW") %>% 
  filter(Partei!="BVB/FW") %>% 
  filter(Partei!="FW") %>% 
  filter(Partei!="fraktionslos") %>% 
  filter(SM_Twitter_user != "") %>% 
  select(Name, Partei, SM_Twitter_user, Geschlecht) %>% 
  mutate(SM_Twitter_user=tolower(SM_Twitter_user))
Parlamentarier_senti$Partei[Parlamentarier_senti$Partei == "CSU"] <- "CDU/CSU"
Parlamentarier_senti$Partei[Parlamentarier_senti$Partei == "CDU"] <- "CDU/CSU"
Parlamentarier_senti$Partei[Parlamentarier_senti$Partei == "FDP/DVP"] <- "FDP" 
load("Data/sentiWS.RData")
sentiws <- dictionary(list(positive=positive, negative=negative))
tweet_corpus <- corpus(filtert_tweets, text_field = "text")
token <- tokens(tweet_corpus)
dfm <- dfm(token)

senti <- dfm_lookup(dfm, sentiws)  
senti <- dfm_group(senti, groups = screen_name)
senti_frame <- senti %>% 
  convert(to= "data.frame") %>% 
  as_tibble() %>% 
  mutate(doc_id=tolower(doc_id))
Fehler in UseMethod("convert") : 
  nicht anwendbare Methode für 'convert' auf Objekt der Klasse "c('dfm', 'dgCMatrix', 'CsparseMatrix', 'dsparseMatrix', 'generalMatrix', 'dCsparseMatrix', 'dMatrix', 'sparseMatrix', 'compMatrix', 'Matrix', 'xMatrix', 'mMatrix', 'Mnumeric', 'replValueSp')" angewendet

Barchart

  • Partei absteigend nach negativem Sentiment sortieren
  • Beschriftung der X- und Y Werte ändern
  • Barchart bilden und in ggplotly konvertieren
senti_bar <- senti_final_frame %>% 
  mutate(Partei = fct_reorder(Partei, positive, max)) %>% 
  pivot_longer(c(positive, negative), values_to = c("Verhältnis"), names_to = c("Äußerung")) %>% 
  ggplot() + 
  geom_bar(aes(Partei,
               Verhältnis,               
               fill = Äußerung),
           position = "stack",
           stat = "identity") +
  scale_fill_manual(values = c("#f46666", "#8EC9BB")) + 
  theme_classic() + 
  labs(
    title = "Äußerungen der Politiker*innen auf Twitter zum Thema 'Gender'",
    subtitle = "Einschätzung durch Sentimentanalyse",
    x = "Partei",
    y = "Verhältnis",
    fill = "Äußerung"
  )
Fehler in mutate(., Partei = fct_reorder(Partei, positive, max)) : 
  Objekt 'senti_final_frame' nicht gefunden

Twitter Netzwerk

tweets_network <- filtert_tweets %>% 
  select(screen_name,
         retweet_screen_name,
         created_at) %>% 
  mutate(screen_name = tolower(screen_name))
parlamentarier_network <- Parlamentarier %>% 
  select(Name,
         Partei,
         SM_Twitter_user) %>% 
  drop_na(SM_Twitter_user) %>% 
  mutate(SM_Twitter_user = tolower(SM_Twitter_user)) %>% 
  rename("screen_name" = "SM_Twitter_user")

network_data <- parlamentarier_network %>% 
  left_join(tweets_network, by = "screen_name")
hauefigkeit_tweets <- network_data %>% 
  count(screen_name)

network_data <- network_data %>% 
  left_join(hauefigkeit_tweets, by = "screen_name")
screen_suche <- parlamentarier_network$screen_name

filtered_retweeters <- network_data %>% 
  filter(str_detect(retweet_screen_name, paste (screen_suche, collapse = "|")))
netz <- filtered_retweeters %>% 
  filter(!is.na(retweet_screen_name)) %>% 
  select(from = screen_name, to = retweet_screen_name) %>% 
  as_tbl_graph

netz <- netz %>% 
  mutate(
    degree = centrality_degree(mode = "in"), 
    component = group_components()
  ) %>% 
  left_join(network_data %>% 
              select(Name, screen_name, Partei, n) %>% distinct(screen_name, .keep_all = T),
            by = c( "name" ="screen_name")) 
partei_farben <- list(
  "CDU/CSU" = "black",
  "SPD" = "red",
  "CSU" = "black",
  "FDP" = "#e2b007",
  "GRÜNE" = "#07C23F",
  "LINKE" = "#de4c8a", 
  "AfD" = "blue"
)

Netzwerk

Zuweisung von Attributen an Nodes: * Parteizugehörigkeit in Farbe * Klarname der Politikerinnen als Label Anzahl der Tweets in Größe

netz_plot <- netz %>% 
  filter(component <= 6) %>% 
  mutate(degree = centrality_degree(mode = "in")) %>% 
  ggraph(layout = "fr") +
  geom_edge_link(alpha = 0.25, arrow = arrow(length = unit("5", "mm"))) +
  geom_node_point(aes(size = n, color = Partei)) + 
  scale_color_manual(values = partei_farben) +
  theme_graph(background = "white") + # theme
  geom_node_label(aes(filter = n >= 15, label = Name), repel = T, show.legend = F, alpha = 0.5) + #position = "jitter"
  labs(title = "Retweet-Netzwerke deutscher Politiker*innen", size = "Anzahl Tweets", subtitle = "zum Thema 'Gender' ab 2016")
netz_plot 

NODES DEUTLICHERE ABSTUFUNG, VLT. THEME FUNKTIONIERT NICHT RICHTIG

LS0tDQp0aXRsZTogIlJfUHJvamVrdF9HZW5kZXIiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sgDQotLS0NCiMgTG9hZCBsaWJyYXJpZXMgDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dGV4dCkgDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocXVhbnRlZGEpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShmb3JjYXRzKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KGxlYWZsZXQubWluaWNoYXJ0cykNCmxpYnJhcnkoc3ApDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkocnR3ZWV0KQ0KbGlicmFyeShnZ3JhcGgpDQpsaWJyYXJ5KGdncmVwZWwpDQpsaWJyYXJ5KHRpZHlncmFwaCkNCmBgYA0KDQojIExvYWQgRGF0YSANCg0KYGBge3J9DQpQYXJsYW1lbnRhcmllciA8LSByZWFkX2NzdigiRGF0YS9QYXJsYW1lbnRhcmllci5jc3YiKQ0KYGBgDQoNCkVVLVBhcmxhbWVudCByYXVzZmlsdGVybg0KYGBge3J9DQpQYXJsYW1lbnRhcmllciA8LSBQYXJsYW1lbnRhcmllciAlPiUgDQogIGZpbHRlcihLYXRlZ29yaWUhPSJFVS1QYXJsYW1lbnQiKQ0KYGBgDQoNClJlYWQgVGFiZWxsZW4gbWl0IGZlaGxlbmRlbiBHZXNjaGxlY2h0ZXJuDQpgYGB7cn0NCmFsbGVfZ2VzY2hsZWNodGVyIDwtIHJlYWRfZXhjZWwoIkRhdGEvUGFybGFtZW50YXJpZXJfZ2VzY2hlY2h0ZXIueGxzeCIsIHNoZWV0ID0gIkdlc2NobGVjaHRlciIpDQpgYGANCg0KSm9pbiBmZWhsZW5kZSBHZXNjaGxlY2h0ZXJhbmdhYmVuIA0KYGBge3J9DQpQYXJsYW1lbnRhcmllciA8LSBQYXJsYW1lbnRhcmllciAlPiUgDQogIGxlZnRfam9pbihhbGxlX2dlc2NobGVjaHRlciwgYnkgPSBjKCJOYW1lIiA9ICJOYW1lIikpDQpgYGANCg0KUmVuYW1lIFNwYWx0ZW4NCmBgYHtyfQ0KUGFybGFtZW50YXJpZXIgPC0gUGFybGFtZW50YXJpZXIgJT4lIA0KICByZW5hbWUoR2VzY2hsZWNodCA9IEdlc2NobGVjaHQueSkNCmBgYA0KDQojIEJ1bmRlc3RhZyBHZXNjaGxlY2h0ZXJ2ZXJ0ZWlsdW5nIHBybyBQYXJ0ZWkNCg0KKiBmaWx0ZXJuIG5hY2ggQnVuZGVzdGFnDQoqIENEVSAmIENTVSB6dXNhbW1lbmbDvGdlbiANCiogbmFjaCBtw6RubmxpY2ggdW5kIHdlaWJsaWNoIGdydXBwaWVyZW4sIEFuemFobCBiZXJlY2huZW4gdW5kIHNvcnRpZXJlbg0KYGBge3J9DQpidW5kZXN0YWcgPC0gUGFybGFtZW50YXJpZXIgJT4lIA0KICBzZWxlY3QoaWQsIE5hbWUsIEdlc2NobGVjaHQsIFBhcnRlaSwgS2F0ZWdvcmllLCBTTV9Ud2l0dGVyX2lkLCBTTV9Ud2l0dGVyX3VzZXIpICU+JSANCiAgZmlsdGVyKEthdGVnb3JpZT09IkJ1bmRlc3RhZyIpDQoNCmJ1bmRlc3RhZyRQYXJ0ZWlbYnVuZGVzdGFnJFBhcnRlaSA9PSAiQ1NVIl0gPC0iQ0RVL0NTVSINCmJ1bmRlc3RhZyRQYXJ0ZWlbYnVuZGVzdGFnJFBhcnRlaT09IkNEVSJdPC0iQ0RVL0NTVSINCg0KYnVuZGVzdGFnX21hbiA8LSBidW5kZXN0YWcgJT4lIA0KICBncm91cF9ieShQYXJ0ZWkpICU+JSANCiAgZmlsdGVyKEdlc2NobGVjaHQgPT0gIm3DpG5ubGljaCIpICU+JSANCiAgc3VtbWFyaXNlKGdlc2FtdF9tYW49bigpKQ0KDQpidW5kZXN0YWdfZ2VzYW10IDwtIGJ1bmRlc3RhZyAlPiUgDQogIGdyb3VwX2J5KFBhcnRlaSkgJT4lIA0KICBmaWx0ZXIoR2VzY2hsZWNodCA9PSAid2VpYmxpY2giKSAlPiUgDQogIHN1bW1hcmlzZShnZXNhbXRfZnJhdT1uKCkpICU+JSANCiAgbGVmdF9qb2luKGJ1bmRlc3RhZ19tYW4pICU+JSANCiAgbXV0YXRlKHJvd19zdW09cm93U3VtcyguWzI6M10pKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhyb3dfc3VtKSkNCmBgYA0KDQojIyMgQmFyY2hhcnQgQnVuZGVzdGFnDQoNCiogU3RhY2tlZC1CYXJjaGFydCB6dXIgR2VzY2hsZWNodGVydmVydGVpbHVuZyBpbSBCdW5kZXN0YWcgcHJvIFBhcnRlaQ0KKiB2aXN1YWxpc2llcnVuZyBkZXIgRGF0ZW4gaW4gbcO2Z2xpY2hzdCBnZXNjaGxlY2h0ZXJuZXVyYWxlbiBGYXJiZW4NCmBgYHtyfQ0KcGxvdF9seShkYXRhPWJ1bmRlc3RhZ19nZXNhbXQsIHg9fnJlb3JkZXIoUGFydGVpLC1yb3dfc3VtKSwgeT1+Z2VzYW10X2ZyYXUsIG5hbWU9IndlaWJsaWNoIiwgbWFya2VyPWxpc3QoY29sb3I9dG9SR0IoIiNGQjdGNjIiKSksDQogICAgICAgIHR5cGU9ImJhciIsIGhvdmVyaW5mbz0idGV4dCIsIHRleHQ9fnBhc3RlKCIiLHJvd19zdW0pLCANCiAgICAgICAgaG92ZXJ0ZW1wbGF0ZT1wYXN0ZSgiQW56YWhsOiAle3k6LC4wZn0iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPkdlc2FtdGFuemFobDogJXt0ZXh0fSIpKSAlPiUgDQogIGFkZF90cmFjZSh5PX5nZXNhbXRfbWFuLCBuYW1lPSJtw6RubmxpY2giLCBtYXJrZXI9bGlzdChjb2xvcj10b1JHQigiIzc3NTI4NSIpKSkgJT4lIA0KICBsYXlvdXQoYmFybW9kZT0ic3RhY2siLCB0aXRsZT0iR2VzY2hsZWNodGVydmVydGVpbHVuZyBpbiBkZW4gUGFydGVpZW4gaW0gQnVuZGVzdGFnIiwgIyB0aXRlbCBwb3NpdGlvbiB1bmQgZ3LDtsOfZSDDpG5kZXJuDQogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiIiksIHlheGlzPWxpc3QodGl0bGU9IkFuemFobCBBYmdlb3JkbmV0ZSIpKSAlPiUNCiAgbGF5b3V0KGxlZ2VuZD1saXN0KHg9MSwgeT0wLjUpKQ0KYGBgDQoNCiMgTGFuZHRhZyBHZXNjaGxlY2h0ZXJ2ZXJ0ZWlsdW5nIHBybyBQYXJ0ZWkNCg0KKiBmaWx0ZXJuIGFsbGVuIExhbmR0YWdlbg0KKiBDRFUgJiBDU1Ugc293aWUgRkRQICYgRFZQIHp1c2FtbWVuZsO8Z2VuIA0KKiBuYWNoIG3DpG5ubGljaCB1bmQgd2VpYmxpY2ggZ3J1cHBpZXJlbiwgQW56YWhsIGJlcmVjaG5lbiB1bmQgc29ydGllcmVuDQpgYGB7cn0NCmxhbmR0YWdfcGFydGVpIDwtIFBhcmxhbWVudGFyaWVyICU+JSANCiAgZmlsdGVyKEthdGVnb3JpZSAhPSJCdW5kZXN0YWciKSAlPiUgDQogIGZpbHRlcihLYXRlZ29yaWUhPSJFVS1QYXJsYW1lbnQiKSAlPiUgDQogIGZpbHRlcihQYXJ0ZWkgIT0iQklXIikgJT4lIA0KICBmaWx0ZXIoUGFydGVpIT0iU1NXIikgJT4lIA0KICBmaWx0ZXIoUGFydGVpIT0iQlZCL0ZXIikgJT4lIA0KICBmaWx0ZXIoUGFydGVpIT0iRlciKQ0KYGBgDQoNCmBgYHtyfQ0KbGFuZHRhZ19wYXJ0ZWkkUGFydGVpW2xhbmR0YWdfcGFydGVpJFBhcnRlaSA9PSAiQ1NVIl0gPC0iQ0RVL0NTVSINCmxhbmR0YWdfcGFydGVpJFBhcnRlaVtsYW5kdGFnX3BhcnRlaSRQYXJ0ZWk9PSJDRFUiXSA8LSAiQ0RVL0NTVSINCmxhbmR0YWdfcGFydGVpJFBhcnRlaVtsYW5kdGFnX3BhcnRlaSRQYXJ0ZWk9PSJGRFAvRFZQIl0gPC0gIkZEUCIgDQpgYGANCg0KYGBge3J9DQpsYW5kdGFnX21hbiA8LSBsYW5kdGFnX3BhcnRlaSAlPiUgDQogIGdyb3VwX2J5KFBhcnRlaSkgJT4lIA0KICBmaWx0ZXIoR2VzY2hsZWNodD09Im3DpG5ubGljaCIpICU+JSANCiAgc3VtbWFyaXNlKGdlc2FtdF9tYW49bigpKQ0KDQpsYW5kdGFnX2dlc2FtdCA8LSBsYW5kdGFnX3BhcnRlaSAlPiUgDQogIGdyb3VwX2J5KFBhcnRlaSkgJT4lIA0KICBmaWx0ZXIoR2VzY2hsZWNodD09IndlaWJsaWNoIikgJT4lIA0KICBzdW1tYXJpc2UoZ2VzYW10X2ZyYXU9bigpKSAlPiUgDQogIGxlZnRfam9pbihsYW5kdGFnX21hbikgJT4lIA0KICAgbXV0YXRlKHJvd19zdW09cm93U3VtcyguWzI6M10pKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhyb3dfc3VtKSkNCmBgYA0KDQojIyMgQmFyY2hhcnQgTGFuZHRhZw0KDQoqIFN0YWNrZWQtQmFyY2hhcnQgenVyIEdlc2NobGVjaHRlcnZlcnRlaWx1bmcgaW4gZGVuIExhbmR0YWdlbiBwcm8gUGFydGVpDQoqIHZpc3VhbGlzaWVydW5nIGRlciBEYXRlbiBpbiBtw7ZnbGljaHN0IGdlc2NobGVjaHRlcm5ldXJhbGVuIEZhcmJlbg0KYGBge3J9DQpwbG90X2x5KGRhdGE9bGFuZHRhZ19nZXNhbXQsIHg9fnJlb3JkZXIoUGFydGVpLC1yb3dfc3VtKSwgeT1+Z2VzYW10X2ZyYXUsIG5hbWU9IndlaWJsaWNoIiwgbWFya2VyPWxpc3QoY29sb3I9dG9SR0IoIiNGQjdGNjIiKSksIA0KICAgICAgICB0eXBlPSJiYXIiLCBob3ZlcmluZm89InRleHQiLCB0ZXh0PX5wYXN0ZSgiIixyb3dfc3VtKSwgDQogICAgICAgIGhvdmVydGVtcGxhdGU9cGFzdGUoIkFuemFobDogJXt5OiwuMGZ9IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5HZXNhbXRhbnphaGw6ICV7dGV4dH0iKSkgJT4lIA0KICBhZGRfdHJhY2UoeT1+Z2VzYW10X21hbiwgbmFtZT0ibcOkbm5saWNoIiwgbWFya2VyPWxpc3QoY29sb3I9dG9SR0IoIiM3NzUyODUiKSkpICU+JSANCiAgbGF5b3V0KGJhcm1vZGU9InN0YWNrIiwgdGl0bGU9Ikdlc2NobGVjaHRlcnZlcnRlaWx1bmcgaW4gZGVuIFBhcnRlaWVuIGluIGRlbiBMYW5kdGFnZW4iLCAjIHRpdGVsIHBvc2l0aW9uIHVuZCBncsO2w59lIMOkbmRlcm4NCiAgICAgICAgIHhheGlzPWxpc3QodGl0bGU9IiIpLCB5YXhpcz1saXN0KHRpdGxlPSJBbnphaGwgQWJnZW9yZG5ldGUiKSkgJT4lDQogIGxheW91dChsZWdlbmQ9bGlzdCh4PTEsIHk9MC41KSkNCmBgYA0KDQojIE1hcCBtaXQgR2VzY2hsZWNodGVydmVydGVpbHVuZyBpbiBkZW4gTGFuZHRhZ2VuDQoNCiogQWxsZSBMYW5kdGFnZSBoZXJhdXNmaWx0ZXJuDQpgYGB7cn0NCnBhcmxhbWVudGFyaWVyX2xhbmR0YWcgPC0gUGFybGFtZW50YXJpZXIgJT4lIA0KICBmaWx0ZXIoS2F0ZWdvcmllICVpbiUgYygiQWJnZW9yZG5ldGVuaGF1cyB2b24gQmVybGluIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICJCYXllcmlzY2hlciBMYW5kdGFnIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICJCcmVtaXNjaGUgQsO8cmdlcnNjaGFmdCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAiSGFtYnVyZ2lzY2hlIELDvHJnZXJzY2hhZnQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkhlc3Npc2NoZXIgTGFuZHRhZyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAiTGFuZHRhZyBCcmFuZGVuYnVyZyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAiTGFuZHRhZyBkZXMgU2FhcmxhbmRlcyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAiTGFuZHRhZyBNZWNrbGVuYnVyZy1Wb3Jwb21tZXJuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICJMYW5kdGFnIE5vcmRyaGVpbi1XZXN0ZmFsZW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIkxhbmR0YWcgUmhlaW5sYW5kLVBmYWx6IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICJMYW5kdGFnIFNhY2hzZW4tQW5oYWx0IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICJMYW5kdGFnIHZvbiBCYWRlbi1Xw7xydHRlbWJlcmciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIk5pZWRlcnPDpGNoc2lzY2hlciBMYW5kdGFnIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICJTw6RjaHNpc2NoZXIgTGFuZHRhZyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAiU2NobGVzd2lnLUhvbHN0ZWluaXNjaGVyIExhbmR0YWciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIlRow7xyaW5nZXIgTGFuZHRhZyIpKQ0KYGBgDQoNCiogVmVrdG9yZW4gdW5kIERhdGFmcmFtZSBlcnN0ZWxsZW4gbWl0IGRlbiBLb29yZGluYXRlbiBhbiBkZW5lbiBkaWUgc3DDpHRlcmVuIFBpZS1DaGFydHMgZml4aWVydCB3ZXJkZW4gc29sbGVuLiAoSW1tZXIgZ2VuYXUgaW0gTWl0dGVscHVua3QgZGVzIEJ1bmRlc2xhbmRlcykNCg0KYGBge3J9DQpLYXRlZ29yaWUgPC0gYygiQWJnZW9yZG5ldGVuaGF1cyB2b24gQmVybGluIiwgDQogICAgICAgICAgICAgICAiQmF5ZXJpc2NoZXIgTGFuZHRhZyIsIA0KICAgICAgICAgICAgICAgIkJyZW1pc2NoZSBCw7xyZ2Vyc2NoYWZ0IiwNCiAgICAgICAgICAgICAgICJIYW1idXJnaXNjaGUgQsO8cmdlcnNjaGFmdCIsIA0KICAgICAgICAgICAgICAgIkhlc3Npc2NoZXIgTGFuZHRhZyIsDQogICAgICAgICAgICAgICAiTGFuZHRhZyBCcmFuZGVuYnVyZyIsIA0KICAgICAgICAgICAgICAgIkxhbmR0YWcgZGVzIFNhYXJsYW5kZXMiLA0KICAgICAgICAgICAgICAgIkxhbmR0YWcgTWVja2xlbmJ1cmctVm9ycG9tbWVybiIsDQogICAgICAgICAgICAgICAiTGFuZHRhZyBOb3JkcmhlaW4tV2VzdGZhbGVuIiwNCiAgICAgICAgICAgICAgICJMYW5kdGFnIFJoZWlubGFuZC1QZmFseiIsDQogICAgICAgICAgICAgICAiTGFuZHRhZyBTYWNoc2VuLUFuaGFsdCIsIA0KICAgICAgICAgICAgICAgIkxhbmR0YWcgdm9uIEJhZGVuLVfDvHJ0dGVtYmVyZyIsIA0KICAgICAgICAgICAgICAgIk5pZWRlcnPDpGNoc2lzY2hlciBMYW5kdGFnIiwgDQogICAgICAgICAgICAgICAiU8OkY2hzaXNjaGVyIExhbmR0YWciLCANCiAgICAgICAgICAgICAgICJTY2hsZXN3aWctSG9sc3RlaW5pc2NoZXIgTGFuZHRhZyIsIA0KICAgICAgICAgICAgICAgIlRow7xyaW5nZXIgTGFuZHRhZyIpDQoNCmxhdCA8LSBjKDUyLjUyMDAwOCwNCiAgICAgICAgIDQ4LjkxNzQzMSwNCiAgICAgICAgIDUzLjA3NDk4MiwNCiAgICAgICAgIDUzLjU1MzgxNSwNCiAgICAgICAgIDUwLjY1MjA1MiwNCiAgICAgICAgIDUyLjQwODQxOCwNCiAgICAgICAgIDQ5LjM5NjQyMywNCiAgICAgICAgIDUzLjYxMjY1MSwNCiAgICAgICAgIDUxLjQzMzIzNywNCiAgICAgICAgIDUwLjExODM0NiwNCiAgICAgICAgIDUxLjk1MDI2NSwNCiAgICAgICAgIDQ4LjY2MTYwNCwNCiAgICAgICAgIDUyLjYzNjcwNCwNCiAgICAgICAgIDUxLjEwNDU0MSwNCiAgICAgICAgIDU0LjIxOTM2NywNCiAgICAgICAgIDUxLjAxMDk4OSkNCg0KbG5nIDwtIGMoMTMuNDA0OTU0LA0KICAgICAgICAgMTEuNDA3OTgwLA0KICAgICAgICAgOC44MDcwODAsDQogICAgICAgICA5Ljk5MTU3NSwNCiAgICAgICAgIDkuMTYyNDM4LA0KICAgICAgICAgMTIuNTYyNDkyLA0KICAgICAgICAgNy4wMjI5NjEsDQogICAgICAgICAxMi40Mjk1OTUsDQogICAgICAgICA3LjY2MTU5NCwNCiAgICAgICAgIDcuMzA4OTUzLA0KICAgICAgICAgMTEuNjkyMjc0LA0KICAgICAgICAgOS4zNTAxMzQsDQogICAgICAgICA5Ljg0NTA3NywNCiAgICAgICAgIDEzLjIwMTczOCwNCiAgICAgICAgIDkuNjk2MTE3LA0KICAgICAgICAgMTAuODQ1MzQ2KQ0KDQpidW5kZXNsYWVuZGVyX2tvb3JkaW5hdGVuIDwtIGRhdGEuZnJhbWUoS2F0ZWdvcmllLCBsYXQsIGxuZykNCmBgYA0KDQoqIERhdGFmcmFtZSBuYWNoIEFuemFobCBkZXIgbcOkbm5saWNoZW4gdW5kIHdlaWJsaWNoZW4gUG9saXRpa2VyKmlubmVuIHBybyBCdW5kZXNsYW5kIGZpbHRlcm4uDQoNCmBgYHtyfQ0KcGFybGFtZW50YXJpZXJfbWFuIDwtIHBhcmxhbWVudGFyaWVyX2xhbmR0YWcgJT4lIA0KICBncm91cF9ieShLYXRlZ29yaWUpICU+JSANCiAgZmlsdGVyKEdlc2NobGVjaHQgPT0gIm3DpG5ubGljaCIpICU+JSANCiAgc3VtbWFyaXNlKG3DpG5ubGljaCA9IG4oKSkNCg0KcGFybGFtZW50YXJpZXJfbGFuZHRhZ19nZXNhbXQgPC0gcGFybGFtZW50YXJpZXJfbGFuZHRhZyAlPiUgDQogIGdyb3VwX2J5KEthdGVnb3JpZSkgJT4lIA0KICBmaWx0ZXIoR2VzY2hsZWNodCA9PSAid2VpYmxpY2giKSAlPiUgDQogIHN1bW1hcmlzZSh3ZWlibGljaCA9IG4oKSkgJT4lIA0KICBsZWZ0X2pvaW4ocGFybGFtZW50YXJpZXJfbWFuKQ0KYGBgDQoNCiogS29vcmRpbmF0ZW4gZGVyIHNww6R0ZXJlbiBQaWUtQ2hhcnRzIHp1ciBIYXVwdHRhYmVsbGUgam9pbmVuDQoNCmBgYHtyfQ0KcGFybGFtZW50YXJpZXJfa29vcmRpbmF0ZW4gPC0gcGFybGFtZW50YXJpZXJfbGFuZHRhZ19nZXNhbXQgJT4lIA0KICBsZWZ0X2pvaW4oYnVuZGVzbGFlbmRlcl9rb29yZGluYXRlbiwgYnkgPSBjKCJLYXRlZ29yaWUiID0gIkthdGVnb3JpZSIpKQ0KYGBgDQoNCiMgTWFwIG1pdCBQaWUtQ2hhcnRzDQoNCiogZ2VzY2hsZWNodGVybmV1dHJhbGUgRmFyYmVuIGbDvHIgZGllIEdlc2NobGVjaHRlcnZlcnRlaWx1bmcgZXJzdGVsbGVuLCBzb3dpZSBkaWUgR3JlbnplbiBkZXIgQnVuZGVzbMOkbmRlciBpbXBvcnRpZXJlbiB1bmQgbWl0IE5hbWVuIHZlcnNlaGVuIChwb3B1cCkuDQpgYGB7cn0NCmNvbG9ycyA8LSBjKCIjRkI3RjYyIiwgIiM3NzUyODUiKQ0KDQpncmVuemVuIDwtIHJlYWRSRFMoIkRhdGEvZ2FkbTM2X0RFVV8xX3NwLnJkcyIpICU+JSANCiAgc3RfYXNfc2YoKQ0KDQpwb3B1cF9idW5kZXNsYWVuZGVyIDwtIHBhc3RlMCgiPHN0cm9uZz5CdW5kZXNsYW5kOiA8L3N0cm9uZz4iLCBncmVuemVuJE5BTUVfMSkNCmBgYA0KDQoqIFBpZS1DaGFydHMgbWl0IEhpbGZlIHZvbiBhZGRNaW5pY2hhcnRzIHp1ciBLYXJ0ZSBhbmdlZsO8Z3QuIA0KDQpgYGB7cn0NCm1hcCA8LSBsZWFmbGV0KCkgJT4lIA0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRDYXJ0b0RCLlZveWFnZXJMYWJlbHNVbmRlcikgJT4lIA0KICBzZXRWaWV3KGxuZyA9IDEwLjQ1MTUyNiwgbGF0ID0gNTEuMTY1NjkxLCB6b29tID0gNS41KSAlPiUgDQogIGFkZFBvbHlnb25zKGRhdGEgPSBncmVuemVuLA0KICAgICAgICAgICAgICBmaWxsQ29sb3IgPSAiZ3JleSIsDQogICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC4xLA0KICAgICAgICAgICAgICB3ZWlnaHQgPSAzLA0KICAgICAgICAgICAgICBjb2xvciA9ICJncmV5IiwNCiAgICAgICAgICAgICAgcG9wdXAgPSBwb3B1cF9idW5kZXNsYWVuZGVyKSAlPiUgDQogIGFkZE1pbmljaGFydHMoDQogICAgcGFybGFtZW50YXJpZXJfa29vcmRpbmF0ZW4kbG5nLCBwYXJsYW1lbnRhcmllcl9rb29yZGluYXRlbiRsYXQsDQogICAgdHlwZSA9ICJwaWUiLA0KICAgIGNoYXJ0ZGF0YSA9IHBhcmxhbWVudGFyaWVyX2tvb3JkaW5hdGVuWywgYygid2VpYmxpY2giLCAibcOkbm5saWNoIildLA0KICAgIGNvbG9yUGFsZXR0ZSA9IGNvbG9ycywNCiAgICB3aWR0aCA9IDQ2LA0KICAgIGhlaWdodCA9IDQ2LA0KICAgIG9wYWNpdHkgPSAwLjgsDQogICAgbGVnZW5kUG9zaXRpb24gPSAiYm90dG9tcmlnaHQiDQogICkNCm1hcA0KYGBgDQoNCiMgVHdlZXRzIGRlciBQb2xpdGlrZXIqaW5uZW4gc2NyYXBlbg0KDQpOaWNodCBhbHMgYXVzZsO8aHJiYXJlbiBDb2RlLCBkYW1pdCBkaWUgRGF0ZW4gbmljaHQgbm9jaG1hbHMgZ2V6b2dlbiB3ZXJkZW4uLi4NCg0KVHdpdHRlcl9hY2NvdW50cyA8LSBQYXJsYW1lbnRhcmllciAlPiUNCiAgZmlsdGVyKEthdGVnb3JpZSE9IkVVLVBhcmxhbWVudCIpICU+JQ0KICBmaWx0ZXIoUGFydGVpIT0iQklXIikgJT4lIA0KICBmaWx0ZXIoUGFydGVpIT0iU1NXIikgJT4lIA0KICBmaWx0ZXIoUGFydGVpIT0iQlZCL0ZXIikgJT4lIA0KICBmaWx0ZXIoUGFydGVpIT0iRlciKSAlPiUgDQogIGZpbHRlcihQYXJ0ZWkhPSJmcmFrdGlvbnNsb3MiKSAlPiUgDQogIGZpbHRlcihTTV9Ud2l0dGVyX3VzZXIgIT0gIiIpDQoNCnRva2VuIDwtIGdldF90b2tlbigpDQp0b2tlbg0KDQphbGxlX3R3ZWV0cyA8LSBsaXN0KCkNCnVzZXJJZExpc3QgPSBsaXN0KFR3aXR0ZXJfYWNjb3VudHMkU01fVHdpdHRlcl91c2VyKQ0KcmwgPC0gcmF0ZV9saW1pdCh0b2tlbiwgInN0YXR1c2VzL3VzZXJfdGltZWxpbmUiKQ0KZm9yKHVzZXIgaW4gdXNlcklkTGlzdFtbMV1dKXsNCiAgYWxsZV90d2VldHNbW3VzZXJdXSA8LSBnZXRfdGltZWxpbmUodXNlciwgbiA9IDIsIGNoZWNrID0gRikNCiAgcHJpbnQoInJhdGUgbGltaXQgcmVtYWluaW5nOiIsIHN0cihybCRyZW1haW5pbmcpKQ0KICBwcmludCgiYXQgdXNlcjoiLCBzdHIodXNlcikpDQogIHJsIDwtIHJsICU+JQ0KICAgIG11dGF0ZShyZW1haW5pbmcgPSByZW1haW5pbmcgLSAxKQ0KICAjIGlmIHJhdGUgbGltaXQgZXhoYXVzdGVkLCB0aGVuIHdhaXQgdG8gcmF0ZSBsaW1pdCByZXNldA0KICBpZiAocmwkcmVtYWluaW5nID09IDVMKSB7DQogICAgcmwgPC0gcmF0ZV9saW1pdCh0b2tlbiwgInN0YXR1c2VzL3VzZXJfdGltZWxpbmUiKQ0KICAgIHByaW50KCJyYXRlIGxpbWl0IGV4Y2VlZGVkLCB3YWl0aW5nIGZvciA5MDBzIGF0IHVzZXIiLCBzdHIodXNlcikpDQogICAgU3lzLnNsZWVwKGFzLm51bWVyaWMocmwkcmVzZXQsICJzZWNzIikpDQogIH0NCn0NCg0KIyMjIE5hY2ggcmVsZXZhbnRlbiBIYXNodGFncyBmaWx0ZXJuIA0KDQoqICJhbGxlX3R3ZWV0cy5yZHMiIGlzdCBkaWUgRGF0ZWkgbWl0IGFsbGVuIGdlem9nZW5lbiBUd2VldHMuDQoqIG5hY2ggcmVsZXZhbnRlbiBUd2VldHMgZmlsdGVybiBtaXQgdm9yaGVyIGJlc3RpbW10ZW4gSGFzaHRhZ3MgKCJUd2l0dGVyLUhhc2h0YWdzLnhsc3giKSwgVHdlZXRzIGluIEtsZWluc2NocmVpYnVuZyBmb3JtYXRpZXJlbiB1bmQgYWxzICJmaW5hbF90d2VldHMucmRzIiBleHBvcnRpZXJlbi4gDQpgYGB7cn0NCmFsbGVfdHdlZXRzIDwtIHJlYWRfcmRzKCJEYXRhL2FsbGVfdHdlZXRzLnJkcyIpDQoNCnN1Y2hiZWdyaWZmZSA8LSByZWFkX2V4Y2VsKCJEYXRhL1R3aXR0ZXItSGFzaHRhZ3MueGxzeCIsIHNoZWV0ID0iVGFiZWxsZTIiKQ0KDQpzdWNoYmVncmlmZmVfdmVjdG9yIDwtIHN1Y2hiZWdyaWZmZSRIYXNodGFncw0KDQphbGxlX3R3ZWV0cyA8LSBiaW5kX3Jvd3MoYWxsZV90d2VldHMpDQoNCmFsbGVfdHdlZXRzIDwtIGFsbGVfdHdlZXRzICU+JSANCiAgbXV0YXRlKHRleHQgPSB0b2xvd2VyKHRleHQpKQ0KDQpmaWx0ZXJ0X3R3ZWV0cyA8LSBhbGxlX3R3ZWV0cyAlPiUgDQogIGZpbHRlcihzdHJfZGV0ZWN0KHRleHQsIHBhc3RlIChzdWNoYmVncmlmZmVfdmVjdG9yLCBjb2xsYXBzZSA9ICJ8IikpKQ0KDQp3cml0ZV9yZHMoZmlsdGVydF90d2VldHMsICJmaW5hbF90d2VldHMucmRzIikNCmBgYA0KDQojIFNlbnRpbWVudGFuYWx5c2UNCg0KKiBuYWNoIGRlbiAiZ3Jvw59lbiIgUGFydGVpZW4gZmlsdGVybg0KKiBhbGxlIFBvbGl0aWtlcippbm5lbiBlbnRmZXJuZW4sIHdlbGNoZSBrZWluZW4gVHdpdHRlcmFjY291bnQgYmVzaXR6ZW4NCiogVXNlcm5hbWVzIGluIEtsZWluc2NocmVpYnVuZyBmb3JtYXRpZXJlbg0KYGBge3J9DQpQYXJsYW1lbnRhcmllcl9zZW50aSA8LSBQYXJsYW1lbnRhcmllciAlPiUgDQogIGZpbHRlcihQYXJ0ZWkhPSJCSVciKSAlPiUgDQogIGZpbHRlcihQYXJ0ZWkhPSJTU1ciKSAlPiUgDQogIGZpbHRlcihQYXJ0ZWkhPSJCVkIvRlciKSAlPiUgDQogIGZpbHRlcihQYXJ0ZWkhPSJGVyIpICU+JSANCiAgZmlsdGVyKFBhcnRlaSE9ImZyYWt0aW9uc2xvcyIpICU+JSANCiAgZmlsdGVyKFNNX1R3aXR0ZXJfdXNlciAhPSAiIikgJT4lIA0KICBzZWxlY3QoTmFtZSwgUGFydGVpLCBTTV9Ud2l0dGVyX3VzZXIsIEdlc2NobGVjaHQpICU+JSANCiAgbXV0YXRlKFNNX1R3aXR0ZXJfdXNlcj10b2xvd2VyKFNNX1R3aXR0ZXJfdXNlcikpDQpgYGANCg0KKiBDRFUgJiBDU1Ugc293aWUgRkRQICYgRFZQIHp1c2FtbWVuZsO8Z2VuIA0KYGBge3J9DQpQYXJsYW1lbnRhcmllcl9zZW50aSRQYXJ0ZWlbUGFybGFtZW50YXJpZXJfc2VudGkkUGFydGVpID09ICJDU1UiXSA8LSAiQ0RVL0NTVSINClBhcmxhbWVudGFyaWVyX3NlbnRpJFBhcnRlaVtQYXJsYW1lbnRhcmllcl9zZW50aSRQYXJ0ZWkgPT0gIkNEVSJdIDwtICJDRFUvQ1NVIg0KUGFybGFtZW50YXJpZXJfc2VudGkkUGFydGVpW1BhcmxhbWVudGFyaWVyX3NlbnRpJFBhcnRlaSA9PSAiRkRQL0RWUCJdIDwtICJGRFAiIA0KYGBgDQoNCiogU2VudGlXUyBpbXBvcnRpZXJlbiwgdW0gcG9zaXRpdmUgdW5kIG5lZ2F0aXZlIFfDtnJ0ZXIgenUga2xhc3NpZml6aWVyZW4NCiogTGlzdGUgenVyIFNlbnRpbWVudGFuYWx5c2UgaXN0IHZvbiBVbml2ZXJzaXTDpHQgTGVpcHppZzogImh0dHBzOi8vd29ydHNjaGF0ei51bmktbGVpcHppZy5kZS9kZS9kb3dubG9hZCINCmBgYHtyfQ0KbG9hZCgiRGF0YS9zZW50aVdTLlJEYXRhIikNCnNlbnRpd3MgPC0gZGljdGlvbmFyeShsaXN0KHBvc2l0aXZlPXBvc2l0aXZlLCBuZWdhdGl2ZT1uZWdhdGl2ZSkpDQpgYGANCg0KKiBDb3JwdXMgYXVzIGRlbiBUZXh0ZW4gZGVyIGdlZmlsdGVydGVuIFR3ZWV0cyBiaWxkZW4NCmBgYHtyfQ0KdHdlZXRfY29ycHVzIDwtIGNvcnB1cyhmaWx0ZXJ0X3R3ZWV0cywgdGV4dF9maWVsZCA9ICJ0ZXh0IikNCmBgYA0KDQoqIHRva2VuaXNpZXJlbiBkZXIgVGV4dGUgdW5kIGJpbGRlbiBlaW5lciBERk0NCiogc2VudGl3cyB1bmQgIlR3ZWV0LVRva2VucyIgenVzYW1tZW5mw7xocmVuLCBzb3dpZSBuYWNoIHNjcmVlbl9uYW1lIGdydXBwaWVyZW4NCg0KYGBge3J9DQp0b2tlbiA8LSB0b2tlbnModHdlZXRfY29ycHVzKQ0KZGZtIDwtIGRmbSh0b2tlbikNCg0Kc2VudGkgPC0gZGZtX2xvb2t1cChkZm0sIHNlbnRpd3MpICANCnNlbnRpIDwtIGRmbV9ncm91cChzZW50aSwgZ3JvdXBzID0gc2NyZWVuX25hbWUpDQpgYGANCg0KKiBERk0genVtIERhdGFmcmFtZSBrb252ZXJ0aWVyZW4NCiogZG9jX2lkIGluIEtsZWluc2NocmVpYnVuZyBmb3JtYXRpZXJlbg0KKiBQYXJsYW1lbnRhcmllcl9zZW50aSBqb2luZW4NCiogR3J1cHBpZXJlbiBuYWNoIFBhcnRlaQ0KKiBTZW50aW1lbnQgZGVyIGVpbnplbG5lbiBVc2VyKmlubmVuIGF1ZiBQYXJ0ZWllYmVuZSB6dXNhbW1lbmZhc3NlbiB1bmQgVmVyaMOkbHRuaXMgcG9zaXRpdmUvbmVnYXRpdmUgYmlsZGVuDQpgYGB7cn0NCnNlbnRpX2ZyYW1lIDwtIHNlbnRpICU+JSANCiAgY29udmVydCh0bz0gImRhdGEuZnJhbWUiKSAlPiUgDQogIGFzX3RpYmJsZSgpICU+JSANCiAgbXV0YXRlKGRvY19pZD10b2xvd2VyKGRvY19pZCkpDQoNCnNlbnRpX2ZpbmFsX2ZyYW1lIDwtIHNlbnRpX2ZyYW1lICU+JSANCiAgZnVsbF9qb2luKFBhcmxhbWVudGFyaWVyX3NlbnRpLCBieT1jKCJkb2NfaWQiPSJTTV9Ud2l0dGVyX3VzZXIiKSkgJT4lIA0KICBncm91cF9ieShQYXJ0ZWkpICU+JSANCiAgc3VtbWFyaXNlKGFjcm9zcyhjKG5lZ2F0aXZlLCBwb3NpdGl2ZSksIH5yb3VuZChzdW0oLiwgbmEucm0gPSBUUlVFKS9zdW0obmVnYXRpdmUgKyBwb3NpdGl2ZSwgbmEucm0gPSBUUlVFICksIGRpZ2l0cyA9IDIpKSkNCmBgYA0KDQojIyMgQmFyY2hhcnQNCg0KKiBQYXJ0ZWkgYWJzdGVpZ2VuZCBuYWNoIG5lZ2F0aXZlbSBTZW50aW1lbnQgc29ydGllcmVuDQoqIEJlc2NocmlmdHVuZyBkZXIgWC0gdW5kIFkgV2VydGUgw6RuZGVybg0KKiBCYXJjaGFydCBiaWxkZW4gdW5kIGluIGdncGxvdGx5IGtvbnZlcnRpZXJlbg0KYGBge3J9DQpzZW50aV9iYXIgPC0gc2VudGlfZmluYWxfZnJhbWUgJT4lIA0KICBtdXRhdGUoUGFydGVpID0gZmN0X3Jlb3JkZXIoUGFydGVpLCBwb3NpdGl2ZSwgbWF4KSkgJT4lIA0KICBwaXZvdF9sb25nZXIoYyhwb3NpdGl2ZSwgbmVnYXRpdmUpLCB2YWx1ZXNfdG8gPSBjKCJWZXJow6RsdG5pcyIpLCBuYW1lc190byA9IGMoIsOEdcOfZXJ1bmciKSkgJT4lIA0KICBnZ3Bsb3QoKSArIA0KICBnZW9tX2JhcihhZXMoUGFydGVpLA0KICAgICAgICAgICAgICAgVmVyaMOkbHRuaXMsICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICBmaWxsID0gw4R1w59lcnVuZyksDQogICAgICAgICAgIHBvc2l0aW9uID0gInN0YWNrIiwNCiAgICAgICAgICAgc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI2Y0NjY2NiIsICIjOEVDOUJCIikpICsgDQogIHRoZW1lX2NsYXNzaWMoKSArIA0KICBsYWJzKA0KICAgIHRpdGxlID0gIsOEdcOfZXJ1bmdlbiBkZXIgUG9saXRpa2VyKmlubmVuIGF1ZiBUd2l0dGVyIHp1bSBUaGVtYSAnR2VuZGVyJyIsDQogICAgc3VidGl0bGUgPSAiRWluc2Now6R0enVuZyBkdXJjaCBTZW50aW1lbnRhbmFseXNlIiwNCiAgICB4ID0gIlBhcnRlaSIsDQogICAgeSA9ICJWZXJow6RsdG5pcyIsDQogICAgZmlsbCA9ICLDhHXDn2VydW5nIg0KICApDQoNCmZpbmFsX3NlbnRpX3Bsb3QgPC0gZ2dwbG90bHkoc2VudGlfYmFyKQ0KZmluYWxfc2VudGlfcGxvdA0KYGBgDQoNCiMgVHdpdHRlciBOZXR6d2VyayANCg0KKiBmaWx0ZXJ0X3R3ZWV0cyAidmVya2xlaW5lcm4iIHVuZCBpbiB0d2VldHNfbmV0d29yayBzcGVpY2hlcm4NCiogc2NyZWVuX25hbWUgaW4gS2xlaW5zY2hyZWlidW5nIGZvcm1hdGllcmVuDQpgYGB7cn0NCnR3ZWV0c19uZXR3b3JrIDwtIGZpbHRlcnRfdHdlZXRzICU+JSANCiAgc2VsZWN0KHNjcmVlbl9uYW1lLA0KICAgICAgICAgcmV0d2VldF9zY3JlZW5fbmFtZSwNCiAgICAgICAgIGNyZWF0ZWRfYXQpICU+JSANCiAgbXV0YXRlKHNjcmVlbl9uYW1lID0gdG9sb3dlcihzY3JlZW5fbmFtZSkpDQpgYGANCg0KKiBQYXJsYW1lbnRhcmllciBEYXRhZnJhbWUgbWl0IHR3ZWV0c19uZXR3b3JrIGpvaW5lbiwgdW0gS2xhcmFtZW4gdW5kIFBhcnRlaXp1Z2Vow7ZyaWdrZWl0IGRlciBQb2xpdGlrZXIqaW5ubmVuIHp1IGJla29tbWVuDQpgYGB7cn0NCnBhcmxhbWVudGFyaWVyX25ldHdvcmsgPC0gUGFybGFtZW50YXJpZXIgJT4lIA0KICBzZWxlY3QoTmFtZSwNCiAgICAgICAgIFBhcnRlaSwNCiAgICAgICAgIFNNX1R3aXR0ZXJfdXNlcikgJT4lIA0KICBkcm9wX25hKFNNX1R3aXR0ZXJfdXNlcikgJT4lIA0KICBtdXRhdGUoU01fVHdpdHRlcl91c2VyID0gdG9sb3dlcihTTV9Ud2l0dGVyX3VzZXIpKSAlPiUgDQogIHJlbmFtZSgic2NyZWVuX25hbWUiID0gIlNNX1R3aXR0ZXJfdXNlciIpDQoNCm5ldHdvcmtfZGF0YSA8LSBwYXJsYW1lbnRhcmllcl9uZXR3b3JrICU+JSANCiAgbGVmdF9qb2luKHR3ZWV0c19uZXR3b3JrLCBieSA9ICJzY3JlZW5fbmFtZSIpDQpgYGANCg0KKiBIw6R1Zmlna2VpdCBkZXIgVHdlZXRzIHp1bSBUaGVtYSBiZXJlY2huZW4gDQoqIEjDpHVmaWdrZWl0IHp1IG5ldHdvcmtfZGF0YSBqb2luZW4NCmBgYHtyfQ0KaGF1ZWZpZ2tlaXRfdHdlZXRzIDwtIG5ldHdvcmtfZGF0YSAlPiUgDQogIGNvdW50KHNjcmVlbl9uYW1lKQ0KDQpuZXR3b3JrX2RhdGEgPC0gbmV0d29ya19kYXRhICU+JSANCiAgbGVmdF9qb2luKGhhdWVmaWdrZWl0X3R3ZWV0cywgYnkgPSAic2NyZWVuX25hbWUiKQ0KYGBgDQoNCiogVmVrdG9yIGRlciBzY3JlZW5fbmFtZXMgYWxsZXIgUG9saXRpa2VyKmlubmVuIGVyc3RlbGxlbg0KKiByZXR3ZWV0X3NjcmVlbl9uYW1lIGF1ZiBkaWVzZSBzY3JlZW5fbmFtZXMgZmlsdGVybiwgdW0gIkZyZW1kdXNlcippbm5lbiIgYXVzenVzY2hsaWXDn2VuIA0KYGBge3J9DQpzY3JlZW5fc3VjaGUgPC0gcGFybGFtZW50YXJpZXJfbmV0d29yayRzY3JlZW5fbmFtZQ0KDQpmaWx0ZXJlZF9yZXR3ZWV0ZXJzIDwtIG5ldHdvcmtfZGF0YSAlPiUgDQogIGZpbHRlcihzdHJfZGV0ZWN0KHJldHdlZXRfc2NyZWVuX25hbWUsIHBhc3RlIChzY3JlZW5fc3VjaGUsIGNvbGxhcHNlID0gInwiKSkpDQpgYGANCg0KKiBOZXR6IG1pdCByZWxldmFudGVuIFdlcnRlbiBkZWZpbmllcmVuIG1pdDogDQoqIEVkZ2VzID0gdm9uIHNjcmVlbl9uYW1lIHp1IHJldHdlZXRfc2NyZWVuX25hbWUNCiogTm9kZXMgPSBQb2xpdGlrZXIqaW5uZW4gKHNjcmVlbl9uYW1lIHVuZCByZXR3ZWV0X3NjcmVlbl9uYW1lKSANCiogSm9pbmUgYXVmcyBOZXR6IFBhcnRlaXp1Z2Vow7ZyaWdrZWl0LCBLbGFybmFtZW4gdW5kIEjDpHVmaWdrZWl0IGRlciBUd2VldHMgenVtIFRoZW1hIChuKQ0KYGBge3J9DQpuZXR6IDwtIGZpbHRlcmVkX3JldHdlZXRlcnMgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKHJldHdlZXRfc2NyZWVuX25hbWUpKSAlPiUgDQogIHNlbGVjdChmcm9tID0gc2NyZWVuX25hbWUsIHRvID0gcmV0d2VldF9zY3JlZW5fbmFtZSkgJT4lIA0KICBhc190YmxfZ3JhcGgNCg0KbmV0eiA8LSBuZXR6ICU+JSANCiAgbXV0YXRlKA0KICAgIGRlZ3JlZSA9IGNlbnRyYWxpdHlfZGVncmVlKG1vZGUgPSAiaW4iKSwgDQogICAgY29tcG9uZW50ID0gZ3JvdXBfY29tcG9uZW50cygpDQogICkgJT4lIA0KICBsZWZ0X2pvaW4obmV0d29ya19kYXRhICU+JSANCiAgICAgICAgICAgICAgc2VsZWN0KE5hbWUsIHNjcmVlbl9uYW1lLCBQYXJ0ZWksIG4pICU+JSBkaXN0aW5jdChzY3JlZW5fbmFtZSwgLmtlZXBfYWxsID0gVCksDQogICAgICAgICAgICBieSA9IGMoICJuYW1lIiA9InNjcmVlbl9uYW1lIikpIA0KYGBgDQoNCiogRmFyYmVuIGbDvHIgUGFydGVpZW4gZGVmaW5pZXJlbg0KYGBge3J9DQpwYXJ0ZWlfZmFyYmVuIDwtIGxpc3QoDQogICJDRFUvQ1NVIiA9ICJibGFjayIsDQogICJTUEQiID0gInJlZCIsDQogICJDU1UiID0gImJsYWNrIiwNCiAgIkZEUCIgPSAiI2UyYjAwNyIsDQogICJHUsOcTkUiID0gIiMwN0MyM0YiLA0KICAiTElOS0UiID0gIiNkZTRjOGEiLCANCiAgIkFmRCIgPSAiYmx1ZSINCikNCmBgYA0KDQojIE5ldHp3ZXJrDQoNClp1d2Vpc3VuZyB2b24gQXR0cmlidXRlbiBhbiBOb2RlczoNCiogUGFydGVpenVnZWjDtnJpZ2tlaXQgaW4gRmFyYmUNCiogS2xhcm5hbWUgZGVyIFBvbGl0aWtlcippbm5lbiBhbHMgTGFiZWwgDQoqIEFuemFobCBkZXIgVHdlZXRzIGluIEdyw7bDn2UNCmBgYHtyfQ0KbmV0el9wbG90IDwtIG5ldHogJT4lIA0KICBmaWx0ZXIoY29tcG9uZW50IDw9IDYpICU+JSANCiAgbXV0YXRlKGRlZ3JlZSA9IGNlbnRyYWxpdHlfZGVncmVlKG1vZGUgPSAiaW4iKSkgJT4lIA0KICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluayhhbHBoYSA9IDAuMjUsIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgiNSIsICJtbSIpKSkgKw0KICBnZW9tX25vZGVfcG9pbnQoYWVzKHNpemUgPSBuLCBjb2xvciA9IFBhcnRlaSkpICsgDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYXJ0ZWlfZmFyYmVuKSArDQogIHRoZW1lX2dyYXBoKGJhY2tncm91bmQgPSAid2hpdGUiKSArICMgdGhlbWUNCiAgZ2VvbV9ub2RlX2xhYmVsKGFlcyhmaWx0ZXIgPSBuID49IDE1LCBsYWJlbCA9IE5hbWUpLCByZXBlbCA9IFQsIHNob3cubGVnZW5kID0gRiwgYWxwaGEgPSAwLjUpICsgI3Bvc2l0aW9uID0gImppdHRlciINCiAgbGFicyh0aXRsZSA9ICJSZXR3ZWV0LU5ldHp3ZXJrZSBkZXV0c2NoZXIgUG9saXRpa2VyKmlubmVuIiwgc2l6ZSA9ICJBbnphaGwgVHdlZXRzIiwgc3VidGl0bGUgPSAienVtIFRoZW1hICdHZW5kZXInIGFiIDIwMTYiKQ0KYGBgDQpgYGB7cn0NCm5ldHpfcGxvdCANCmBgYA0KDQpOT0RFUyBERVVUTElDSEVSRSBBQlNUVUZVTkcsIFZMVC4gVEhFTUUgRlVOS1RJT05JRVJUIE5JQ0hUIFJJQ0hUSUcNCg==